Android Binder 浅析
开头废话
先说一下为什么要写这篇博客吧,起源于我对主席的书进行二周目的时候,看到了第二章IPC,然后去试着手写了一遍AIDL,再然后就有点困惑这里面是啥玩意,再然后就发现这里面的坑有点大了。。从AIDL
到Binder
,然后看到了Android系统里面那么多的binder使用,又去翻了一点activity
的启动,从中又看到了Binder
在进程间通讯的影子,从AMS
到activityThread
的通信等等。我觉得是很有必要写一些我的认识,可能还是有些云里雾里的,就当了解吧。
从AIDL开始
先说一下平时写AIDL都是怎么写的,用主席的那个例子
- 定义一个可序列化的实体类
这个就不多说了,这里选择实现Parcelable
接口来进行序列化,
|
|
这个类有两个字段,明白意思就好了。
2.定义Book
的aidl文件
虽然是在同一个包里面,但是还是要去声明一个这样的文件
|
|
3.定义AIDL接口
|
|
这里注意import
Book进来,虽然是一个包里面的。
4.build一下,然后在build/generated/source/aidl/debug/com.demos.aidl
x里面就有了个IBookManager
这么一个JAVA文件了。这就是系统帮助我们生成的。
5.使用
在client
端
然后在Server
端
|
|
这样就基本是一个很简单的进程间通信了。前面是系统给我们自动生成的一个IBookManager
接口,这个接口里面都是些什么东西呢。进去看看的话,刚开始看不出什么东西,(因为它的排版很烂啊),下面来排个版简单的分析一下这东西。
分析aidl生成接口
首先它是一个接口public interface IBookManager extends android.os.IInterface
,要去进行进程间通信,都要去遵循IInterface
这个接口,然后里面还有一个很重要的类public static abstract class Stub extends android.os.Binder implements com.demos.aidl.IBookManager
这个内部类继承自Binder
还实现了我们定义的aidl接口。回过头看在客户端是怎么获得IBookManager
的?IBookManager.Stub.asInterface(service);
下面就是实现的代码:
|
|
下面去看看这个代理类
|
|
其实这个东西什么正事没干,就是把参数什么的传递给远程真正干活的,真正干活的在哪?还是在前面的那个Stub
类里面,有一个onTransact
方法,参数和上面的mRemote.transact
方法的参数相对应,看一下吧
|
|
这样基本上就大概理清楚了,手写AIDL
就不写了,写出来基本上是一样的。
概述多进程
在Android中,打开一个APP可以认为是开启了一个进程,当然四大组件也可以再开新的进程,不同的进程之间是不同的虚拟机,所以可以认识他们互相不知道对方的存在,也就没法去直接的交流。那么如果想要跨进程通信,其实有很多的办法,比如管道,System V,Socket等,那么Android为什么还要去自己搞一个binder呢,既然自己费那么大劲搞了一个,肯定有无可比拟的优势,这里我只知道由于性能和安全的考虑,它就选择了binder😂。
binder基本模型
既然两个进程之间无法直接通信,那么怎么办。在Linux中,内核是可以获取两个进程的全部信息的,那么就好办了,把进程a想说的话告诉内核,内核再告诉b就好了。。。其实实现起来还是很复杂的。好吧,正经点说,首先系统有一个ServiceManager
的进程,简称SM,这个是负责管理Service
的,当一个Server
建立的时候,会去和SM通信,在SM里面进行注册,比如我是张三,我的地址是0x12138。当Client
想要和Server
进行通信的时候,先去询问SM,我怎么联系张三,SM告诉它这个号码,然后就去联系了。其实还是有点不恰当,,大概就这么理解吧。这里注意到,SM其实也是一个进程,一个Server
想要和它进行通信就涉及到了跨进程通信的问题,这就成了鸡-蛋的问题,其实这两者之间也是使用了Binder
,不过系统给我们预先造了一个”鸡”,和普通的跨进程通信还不太一样,这里理解就好。
Binder跨进程原理
这里还是简单的说,因为我也不是特别的理解。就拿上面举的那个book的例子开始说,可以看到Stub
是继承自Binder
并且实现了IBookManager
接口,而Binder
是实现了IBinder
接口,其实在Binder
里面有一个内部类BinderProxy
,它也是实现了IBinder
接口。这样在来看在Client
里面的使用,public void onServiceConnected(ComponentName className, IBinder service)
这里的service
是sub
呢还是BinderProxy
呢,在前面说了,如果是在同一个进程,就返回Server
里面的Binder
本体,否在就返回一个BinderProxy
对象,在asInterface(android.os.IBinder obj)
这个obj
如果是跨进程的,肯定是一个BinderProxy
对象了,然后就去调用这个对象的transact
方法,把需要调用方法的code,数据,返回值,还有一个是不是双向RPC的flag位传进去,我们看一下在BinderProxy
里面,这个方法是怎么实现的
|
|
可以看到是调用了Native
的方法,后面就是一些cpp的东西,我也看不太懂,也就是进去了Binder
驱动里面去折腾东西,然后Binder
驱动把那些参数什么的给Server
的onTransact
方法,然后在根据code去调用Server
中重写的IBookManager
中的方法。然后在onTransact
方法中最后再把写好的返回参数在返回给Binder
驱动,可以在最后看到return super.onTransact(code, data, reply, flags);
,再然后就是Binder
驱动把结果告诉等待的Client
端。这样就基本完成了一次跨进程通信。
这里稍微总结一下,可以看到,无论是sub
还是BinderProxy
都是实现了IBinder
接口,以至于客户端根本无法分辨出是不是在进行跨进程通信,也就是说Client
看起来就和同进程的Server
进行通信一样,只需要轻轻松松的调用getBookList
就能获得想要的结果,也不用去在乎Server
是在什么地方,底层了一切Binder
驱动都给我们封装好了。还有就是在跨进程通信的时候,并没有把方法传过去,仅仅是传递了一个方法的code,然后在Server
中根据这个code来调用对应的方法.
在Android中的体现
在Android的源码中,使用Binder
来进行进程间通讯也是很常见的,来看一个关于启动activity
的东西
|
|
这个三个类的继承实现关系是不是有点眼熟,对,就和前面写的那个book例子一样的,ActivityManagerProxy
就相当于那个内部类Proxy
,ActivityManagerNative
就相当于里面的Stub
,而实现类ActivityManagerService
简称AMS就是Server
了。在细说一下,在执行startActivity
的时候,会到mInstrumentation.execStartActivity()
,就是凯子哥说的那个老板娘😂,然后就是ActivityManagerNative.getDefault().startActivity
,这里getDefault
返回一个IActivityManager
对象,就是使用asInterface(IBinder obj)
获得的,是不是更熟悉了,就是前面判断是不是同一个进程的地方,这里肯定是跨进程了,返回的是一个ActivityManagerProxy
对象,然后在ActivityManagerProxy
里面有startActivity
方法,里面有这么一句mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
又熟悉了吧。好吧,就说到这里了。
结尾
以上只是我自己了解的浅见,肯定有不合理的地方,也只是我自己的理解,以后还会继续学习这方面的东西,毕竟现在也只是一知半解的。
参考:
【凯子哥带你学Framework】Activity启动过程全解析
Android Bander设计与实现 - 设计篇 没看太明白😂